#include <vector>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;

#define _USE_MATH_DEFINES
#include <math.h>

#include <d3dx10math.h>

#include <CL/opencl.h>

#include "..\\PGL\\PGL.h"
using namespace PGL;

float width = 640.0f;
float height = 480.0f;

void printD3DXVECTOR3(D3DXVECTOR3 &input)
{
	cout << "< " << input[0] << " , " << input[1] << " , " << input[2] << " > " << endl;
}

void printD3DXVECTOR4(D3DXVECTOR4 &input)
{
	cout << "< " << input[0] << " , " << input[1] << " , " << input[2] << " , " << input[3] << " >" << endl;
}

void printD3DXMATRIX(D3DXMATRIX &input)
{
	for(int r=0; r<4; r++)
	{
		cout << "| ";
		for(int c=0; c<4; c++)
		{
			cout << input.m[r][c] << " ";
		}
		cout << "|" << endl;
	}
}

void dxtest()
{
	// Initialize the view matrix
	D3DXVECTOR3 Eye( 0.0f, 0.0f, -10.0f );
	D3DXVECTOR3 At( 0.0f, 0.0f, 0.0f );
	D3DXVECTOR3 Up( 0.0f, 1.0f, 0.0f );
	D3DXMATRIX dxView;
	D3DXMatrixLookAtLH(&dxView,&Eye,&At,&Up);

	D3DXMATRIX dxProjection;
    D3DXMatrixPerspectiveFovLH(&dxProjection, (float)D3DX_PI * 0.25f, (float)width/(float)height, 0.1f, 100.0f);

	D3DXMATRIX dxViewProjection;
	D3DXMatrixMultiply(&dxViewProjection,&dxView,&dxProjection);

	D3DXVECTOR3 input(0,1,0);

	D3DXVECTOR4 inputViewProjTransformed;
	D3DXVec3Transform(&inputViewProjTransformed,&input,&dxViewProjection);
	
	cout << "DirectX10 Output: " << endl;
	printD3DXVECTOR3(input);
	printD3DXVECTOR4(inputViewProjTransformed);
	printD3DXMATRIX(dxViewProjection);
}

void pgltest()
{
	// Initialize PGL 
	PGL::CLGL *clgl;
	clgl = new PGL::CLGL();
	cout << clgl->getCurrentErrorAsString() << endl;

	clgl->resize(width,height);
	cout << clgl->getCurrentErrorAsString() << endl;

	// Initialize the view matrix
	pglVector Eye( 0.0f, 0.0f, -10.0f );
	pglVector At( 0.0f, 0.0f, 0.0f );
	pglVector Up( 0.0f, 1.0f, 0.0f );
	pglMatrix pglView;
	matrixLoadLookAt(&pglView,Eye,At,Up);

	// Projection matrix
	pglMatrix pglProjection;
	matrixLoadPerspectiveProjection(&pglProjection,M_PI_4,(float)width/(float)height, 0.1f, 100.0f);

	// Viewport matrix
	pglMatrix pglViewport;
	matrixLoadViewportTransform(&pglViewport,0,0,width,height,0.0f,1.0f);

	// Input vertex
	pglNormalVertex input(pglVector(0,1,0),pglVector(0,1,0));

	// Mem objects
	pglMemObject verticesObj = clgl->createMemObject(&input,sizeof(pglNormalVertex));
	cout << clgl->getCurrentErrorAsString() << endl;

	pglMemObject verticesOutObj = clgl->createMemObject(NULL,sizeof(pglNormalVertex));
	cout << clgl->getCurrentErrorAsString() << endl;

	pglMemObject transformMatrixObj = clgl->createMemObject(NULL,sizeof(pglMatrix));
	cout << clgl->getCurrentErrorAsString() << endl;

	// Set Arguments
	cl_int numMatrices = 1;
	clgl->vertexTransformState.arguments[clgl->vertexTransformState.argumentNames["vertices"]].argptr = &(verticesObj.memObject);
	clgl->vertexTransformState.arguments[clgl->vertexTransformState.argumentNames["outVertices"]].argptr = &(verticesOutObj.memObject);
	clgl->vertexTransformState.arguments[clgl->vertexTransformState.argumentNames["matrices"]].argptr = &(transformMatrixObj.memObject);
	clgl->vertexTransformState.arguments[clgl->vertexTransformState.argumentNames["numMatrices"]].argptr = &(numMatrices);

	clgl->vertexTransformState.dimensions = 1;
	clgl->vertexTransformState.globalWorkSize = new cl_uint[1];
	clgl->vertexTransformState.globalWorkSize[0] = 1;
	clgl->vertexTransformState.localWorkSize = NULL;

	// Set objects
	clgl->remapMemObject(transformMatrixObj);
	pglMatrix pglViewProjection = pglView*pglProjection*pglViewport;
	memcpy(transformMatrixObj.hostPtr,&pglViewProjection,sizeof(pglMatrix));

	// Unmap mem objects
	clgl->unmapMemObject(transformMatrixObj);
	clgl->unmapMemObject(verticesObj);
	clgl->unmapMemObject(verticesOutObj);
	cout << clgl->getCurrentErrorAsString() << endl;

	// Set default args
	clgl->setDefaultPipelineArgs();
	cout << clgl->getCurrentErrorAsString() << endl;

	// RUN THE PIPELINE
	clgl->runPipeline();
	cout << clgl->getCurrentErrorAsString() << endl;

	// Remap everything
	clgl->remapMemObject(transformMatrixObj);
	clgl->remapMemObject(verticesObj);
	clgl->remapMemObject(verticesOutObj);
	cout << clgl->getCurrentErrorAsString() << endl;

	cout << "PGL Output: " << endl;
	
	cout << "View matrix: " << endl;
	for(int i=0; i<4; i++)
	{
		cout << pglView[i] << endl;
	}

	cout << "Projection matrix: " << endl;
	for(int i=0; i<4; i++)
	{
		cout << pglProjection[i] << endl;
	}

	pglMatrix *viewProjPtr = (pglMatrix*)(transformMatrixObj.hostPtr);
	cout << "ViewProjection matrix" << endl;
	for(int i=0; i<4; i++)
	{
		cout << (*viewProjPtr)[i] << endl;
	}

	pglNormalVertex *inputPtr = (pglNormalVertex*)(verticesObj.hostPtr);
	pglNormalVertex *outputPtr = (pglNormalVertex*)(verticesOutObj.hostPtr);

	cout << "Results: " << endl;
	cout << inputPtr->getPosition() << endl;
	cout << outputPtr->getPosition() << endl;
}

int main(char argc, char** argv)
{
	char inChar;

	dxtest();
	pgltest();



	cout << "Type a character and press enter to exit" << endl;
	cin >> inChar; 

	return 0;
}